<?php
/**
 *  Configurable properties of Yz_Model_Field_*:
 *
 *
 */
abstract class Yz_Model_Field_Abstract
    extends Yz_Object
    implements Yz_Model_Field_Interface
{

    const DEFAULTVALUE = null;
    const NOTNULL = false;
    const READONLY = false;
    protected $defaultvalue;
    protected $notnull;
    protected $readonly;
    protected $usedValue;

    /***************************
     *      Public methods     *
     ***************************/


    /**
     *  @param array|null config
     *  @param varargs
     */
    public function __construct($config=null)
    {
        debug();
        $this->init(func_get_args());
    }


    /** Initialization value
     *  @return
     */
    public function newValue()
    {
        $value = $this->usedValue;
        if (null === $value)
            $value = $this->getDefaultValue();
        return $value;
    }

    /** Use value for adding item via form
     *  @return this
     */
    public function useValue($value)
    {
        return $this->usedValue = $this->testValue($value);
    }


    /** Initialization value
     *  @return
     */
    public function assignValue($value)
    {
        if (null === $value)
            $value = $this->getDefaultValue();
        return $value;
    }

    /**
     *  @param mixed value
     *  @return this
     *  @throw Error
     */
    public function testValue($value)
    {
        if (null !== $value)
            $value = $this->_testValue($value);
        if (null === $value)
            if ($this->getNotNull())
                throw NullValueError::_new_();
        return $value;
    }

    /** Whether a null value is allowed
     *  look up a config value `notNull', default to FALSE, which will be inverted
     *  @return bool
     */
    public function getNotNull()
    {
        $r =& $this->notnull;
        if (null === $r) {
            if (isset($this->__dict__['notnull'])) {
                $r = $this->__dict__['notnull'];
                assert('true === $r or false === $r');
            } else {
                $r = $this::NOTNULL;
            }
        }
        return $r;
    }


    /** Whether a value can be changed
     *  look up a config value `readonly', default to FALSE
     *  @return bool
     */
    public function getReadonly()
    {
        $r =& $this->readonly;
        if (null === $r) {
            if (isset($this->__dict__['readonly'])) {
                $r = $this->__dict__['readonly'];
                assert('true === $r or false === $r');
            } else {
                $r = $this::READONLY;
            }
        }
        return $r;
    }



    /** Get a default value
     *  look up a config value defaultvalue', default to self.DEFAULTVALUE
     *  @return mixed
     */
    public function getDefaultValue()
    {
        $r =& $this->defaultvalue;
        if (null === $r) {
            if (isset($this->__dict__['defaultvalue'])) {
                $r = $this->__dict__['defaultvalue'];
            } else {
                $r = $this::DEFAULTVALUE;
            }
        }
        return $r;
    }


    /** Set a default value
     *
     *  @return bool
     */
    public function setDefaultValue($value)
    {
        $r =& $this->defaultvalue;
        // ### ???
        return $this;
    }


    /** init config from arguments to __construct
     *  @param array $config which is (expected to be) func_get_args() of __construct
     */
    protected function init(array $config)
    {
        // use optional config
        if ($config) {
            // if first arg is array, use it as config, but also check other args
            if (is_array($config[0])) {
                $rc = array_change_key_case($config[0], CASE_LOWER);
                unset($config[0]);
            } else {
                $rc = array();
            }
            // if there are other args, each one is treated as a TRUE value for the key=arg
            if ($config)
                foreach ($config as $opt)
                    $rc[strtolower($opt)] = true;
            $this->__dict__ = $rc;
        }
        return $this;
    }

    /**
     *
     */
    protected function fetchConfig($name, $default=null)
    {
        if (isset($this->__dict__[$name]))
            return $this->__dict__[$name];
        return $default;
    }


    /**
     *  @param mixed $value - except null
     *  @return
     *  @throw TypeError if value not scalar
     */
    protected function _testValue($value)
    {
        debug();
        if (! is_scalar($value))
            throw TypeError::_new_();
        if ("" === $value)
            $value = null;
        return $value;
    }

}
